package application

import (
	"errors"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/yyangl/yoyo/registry"
	"github.com/yyangl/yoyo/registry/etcd"
	"github.com/yyangl/yoyo/utils"
	"strings"
	"sync"
)

type Application struct {
	engine     *gin.Engine
	version    string
	addr       string
	services   map[string]*Point
	registers  []registry.Register
	serviceKey string
	mux        *sync.RWMutex
}

func (app *Application) SetEngine(engine *gin.Engine) {
	app.engine = engine
}

func (app *Application) Use(middlewares ...gin.HandlerFunc) {
	app.engine.Use(middlewares...)
}

func (app *Application) SetVersion(version string) {
	app.version = version
}

func (app *Application) SetAddr(adds string) {
	app.addr = adds
}

func (app *Application) Start() error {
	app.mux = &sync.RWMutex{}
	for _, c := range modules {

		c.SetApplication(app)
		c.BootStrap()
	}
	go app.watch()
	return nil
}

func (app *Application) GetRouterGroup(version string) *gin.RouterGroup {
	return app.engine.Group("/" + version)
}

func (app *Application) Run() error {
	if err := app.Start(); err != nil {
		return err
	}
	return app.engine.Run(app.addr)
}

func (app *Application) SetServiceKey(key string) {
	app.serviceKey = key
}

/**
watch service
*/
func (app *Application) watch() {
	//if app.register == nil {
	//	return errors.New("service register not be null")
	//}
	reg, err := etcd.NewEtcdRegister(registry.DefaultAddr)
	if err != nil {
		return
	}
	if app.serviceKey == "" {
		fmt.Printf("service register key not be null\n")
		return
	}

	if app.services == nil {
		app.services = make(map[string]*Point)
	}

	kvs, err := reg.GetService(app.serviceKey)
	if err != nil {
		fmt.Errorf("get services error . err = %v\n", err)
		return
	}

	// send signal . when delete key close goroutine
	chs := make(map[string]chan interface{})

	for k, _ := range kvs {
		keys := strings.Split(k, "/")
		sName := keys[2]
		app.mux.Lock()
		if _, ok := app.services[sName]; !ok {
			ch := make(chan interface{})
			chs[sName] = ch
			app.services[sName] = NewPoint()
			fmt.Printf("go watch !ok sName %s\n", sName)
			go app.watchService(sName, ch)
		}
		app.mux.Unlock()
		//go func() {
		//	app.register.Watch()
		//}()
	}
	go func(reg registry.Register) {
		i := 0
		for resp := range reg.Watch(app.serviceKey) {
			i++
			fmt.Printf("i is %d\n", i)
			switch resp.Event {
			case registry.PUT:
				app.mux.Lock()
				fmt.Printf("i is %d\n", i)
				keys := strings.Split(resp.Key, "/")
				sName := keys[2]
				if _, ok := app.services[sName]; !ok {
					ch := make(chan interface{})
					chs[sName] = ch
					app.services[sName] = NewPoint()
					fmt.Printf("go watch !ok sName %s\n", sName)
					go app.watchService(sName, ch)
				}
				app.mux.Unlock()
				//ch := make(chan interface{})
				//chs[sName] = ch
				//app.services[sName] = NewPoint()
				//go app.watchService(sName, ch)
			case registry.DELETE:
				//_ = p.Delete(resp.Key)
			}
		}

	}(reg)
	select {}
}

func (app *Application) watchService(name string, ch chan interface{}) {
	reg, err := etcd.NewEtcdRegister(registry.DefaultAddr)

	if err != nil {
		return
	}
	kvs, err := reg.GetService(utils.ResolveService(name))
	p := app.services[name]
	for k, v := range kvs {
		keys := strings.Split(k, "/")
		p.Append(keys[3], v)
	}
	fmt.Printf("watch service %s\n", name)
	for resp := range reg.Watch(utils.ResolveService(name)) {
		switch resp.Event {
		case registry.PUT:
			fmt.Printf("put service %s value %s\n", resp.Key, resp.Value)
			kvs := strings.Split(resp.Key, "/")
			p.Append(kvs[3], resp.Value)
		case registry.DELETE:
			kvs := strings.Split(resp.Key, "/")
			fmt.Printf("delete service %s\n", resp.Key)
			_ = p.Delete(kvs[3])
		}
	}
	select {
	case <-ch:
		fmt.Printf("%s server exit")
	}
}

func (app *Application) GetService(name string) (string, string, error) {
	if _, ok := app.services[name]; !ok {
		return "", "", errors.New("service not found")
	}
	p := app.services[name]
	return p.Get()
}
